package breadbox.dataManagement;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.TreeSet;

import breadbox.pastry.ID;


//TODO: create some sort of cache
//look into how file name lookups are performed, see if creating File objects is a good idea
public class DiskStorage implements Storage{
	
	//create/open directory "c:\breadbox"
	private File breadboxDir;
	private String fileSeparator;
	
	public DiskStorage(String path) throws IOException{
		//boolean created = false;
		fileSeparator = System.getProperty("file.separator");
		try {
			breadboxDir = new File(path);
		}
		catch(Exception e) {
			throw new IOException("Could not instantiate file object for disk storage");
		}
			
		if(!breadboxDir.exists() || !breadboxDir.isDirectory()) {
			try {
				System.out.println("creating new directory");
				breadboxDir.mkdir();
			}
			catch(Exception e) {
				throw new IOException("Could not create breadbox directory");
			}
		}
	}
	
	public boolean exists(ID id) {
		return (new File(breadboxDir.getAbsolutePath()+fileSeparator+id)).exists();
	}

	public synchronized byte[] get(ID id) throws IOException {
		File f = new File(breadboxDir.getAbsolutePath()+fileSeparator+id);
		if(!f.exists()) {
			throw new FileNotFoundException("File " + id + " does not exist");
		}
		FileInputStream fis;
		try {
			fis = new FileInputStream(f);
		}
		catch(Exception e) {
			throw new IOException("file input stream could not be instantiated");
		}
		byte[] data = new byte[(int)(f.length())];
		try {
			fis.read(data);
		}
		catch(Exception e) {
			throw new IOException("could not read data from file");
		}
		fis.close();
		return data;
	}
	
	public int write(ID id, byte[] data) throws IOException{
		File f = new File(breadboxDir.getAbsolutePath()+fileSeparator+id);
		if(f.exists()) {
			//throw new IOException("File " + id + " already exists");
			System.err.println("File " + id + " already exists");
		}
		FileOutputStream fos;
		try {
			fos = new FileOutputStream(f);
		}
		catch(Exception e) {
			throw new IOException("file output stream could not be instantiated");
		}
		try {
			fos.write(data);
		}
		catch(Exception e) {
			//throw new IOException("could not write data to file");
			System.err.println("could not write data to file");
		}
		fos.close();
		return 0;
	}
	
	public void delete(ID id) throws IOException{
		File f = new File(breadboxDir.getAbsolutePath()+fileSeparator+id);
		if(!f.exists()) {
			throw new IOException("Cannot delete, file " + id + " doesn't exist");
		}
		try {
			if(!f.delete()) {
				throw new IOException();
			}
		}
		catch(Exception e) {
			throw new IOException("file could not be deleted");
		}
	}
	
	public TreeSet<Long> allChunks() {
		TreeSet<Long> ts = new TreeSet<Long>();
		String[] list = breadboxDir.list();
		for(String s : list) {
			long l;
			try {
				l = Long.decode("0x"+s);
			}
			catch(NumberFormatException n) {
				System.err.println("could not convert filename to long");
				continue;
			}
			ts.add(l);
		}
		return ts;
	}
	
	public void deleteIfOld(ID id, long timeout) {
		File f = new File(breadboxDir.getAbsolutePath()+fileSeparator+id);
		if(!f.exists()) {
			return;
		}
		if(System.currentTimeMillis()-f.lastModified()>timeout) {
			try {
				this.delete(id);
			}
			catch(Exception e) { }
		}
	}
	
	public void touch(ID id) {
		File f = new File(breadboxDir.getAbsolutePath()+fileSeparator+id);
		if(f.exists()) {
			f.setLastModified(System.currentTimeMillis());
		}
	}

}
